﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using NHibernate;
using System.Data;
using NHibernate.Linq;
using SaladBar.Service.Repositories.NHibernateRepository.SessionManagers;
using System.Linq.Expressions;
using SaladBar.Service.Repositories;
using SaladBar.Domain;

namespace SaladBar.Service.Repositories.NHibernateRepository
{
    public class NHibernateRepository<T> : IRepository<T> where T : PersistentObject
    {
        private ISessionManager _sessionManager;

        public NHibernateRepository(ISessionManager sessionManager)
        {
            this._sessionManager = sessionManager;
        }

        public T Get(int id)
        {
            return this._sessionManager.GetCurrentSession().Linq<T>().SingleOrDefault(t => t.Id == id);
        }

        public T Get(Func<T, bool> where)
        {
            return this._sessionManager.GetCurrentSession().Linq<T>().SingleOrDefault(where);
        }

        public IEnumerable<T> GetMany(Func<T, bool> where)
        {
            return this._sessionManager.GetCurrentSession().Linq<T>().Where(where).AsEnumerable();
        }

        public IEnumerable<T> Query(Func<IQueryable<T>, IQueryable<T>> query)
        {
            IQueryable<T> queryable =  this._sessionManager.GetCurrentSession().Linq<T>();
            return query(queryable).AsEnumerable();
        }

        public int GetTotalCount(Func<T, bool> where)
        {
            IQueryable<T> queryable = this._sessionManager.GetCurrentSession().Linq<T>();
            return queryable.Count(where);
        }

        public PagedQueryResult<T> PagedQuery(Func<T, bool> where, Func<IQueryable<T>, IQueryable<T>> orderBy, int pageSize, int pageNumber)
        {
            IQueryable<T> queryable = this._sessionManager.GetCurrentSession().Linq<T>().Where(where).AsQueryable();
            IEnumerable<T> queryResult = orderBy(queryable).Skip(pageSize * (pageNumber - 1)).Take(pageSize).AsEnumerable();
            int totalCount = this.GetTotalCount(where);
            Func<IQueryable<T>, IQueryable<T>> query;
            if (orderBy != null)
                query = (q) => orderBy(q.Where(q2 => where(q2))).Skip(pageSize * (pageNumber - 1)).Take(pageSize);
            else
                query = (q) => q.Where(q2 => where(q2)).Skip(pageSize * (pageNumber - 1)).Take(pageSize);

            return new PagedQueryResult<T>(queryResult, totalCount, pageNumber, pageSize, query);
        }

        public void Save(PersistentObject e)
        {
            ISession session = this._sessionManager.GetCurrentSession();
            ITransaction trans = session.BeginTransaction();
            session.SaveOrUpdate(e);
            session.Flush();
            trans.Commit();
        }

        public void Delete(PersistentObject e)
        {
            ISession session = this._sessionManager.GetCurrentSession();
            ITransaction trans = session.BeginTransaction();
            session.Delete(e);
            session.Flush();
            trans.Commit();
        }

        public IEnumerable<C> QueryForChildren<C>(Func<T,IEnumerable<C>> childSelector, Func<C, bool> childWhere)
        {
            var children = this._sessionManager.GetCurrentSession().Linq<T>().SelectMany(childSelector, (t, c) => new { t = t, c = c });
            return children.Where(child => childWhere(child.c)).Select(child => child.c).AsEnumerable();
        }

        public int QueryForChildrenTotalCount<C>(Func<T, IEnumerable<C>> childSelector, Func<C, bool> childWhere)
        {
            var children = this._sessionManager.GetCurrentSession().Linq<T>().SelectMany(childSelector, (t, c) => new { t = t, c = c });
            return children.Where(child => childWhere(child.c)).Count();
        }
    }
}
